package org.hit.burkun.network;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.Map.Entry;

import org.hit.burkun.entities.Entity;
import org.hit.burkun.file.FileHelper;
import org.hit.burkun.network.models.StaticMethods;
import org.hit.burkun.network.tester.ValidData;
import org.hit.burkun.umls.relation.UniformRelation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class GraphFeatures {
	private static Logger log = LoggerFactory.getLogger(GraphFeatures.class);

	public static void main(String[] args) {
//		SerializableGraph sg = StaticMethods.loadGraphNoDir();
//		countNodeAndEdges(sg);
//		sg = getBubbledGeneGraph(sg, Entity.TYPE.DISEASE_ONTOLOGY,UniformRelation.TYPE.DO_IS_A, 2,  0.1);
//		countNodeAndEdges(sg);
		//统计个数
//		//105875,797284
		SerializableGraph sgDir = StaticMethods.loadGraphDir();
//		countNodeAndEdges(sgDir);
//		//78786, 986021
//		SerializableGraph sgNoDir = StaticMethods.loadGraphNoDir();
//		countNodeAndEdges(sgNoDir);
		
		Integer[] dg = getDegreeDis(sgDir, 20000);
		FileHelper.writeFile("data/degree.txt", Arrays.asList(dg));
	}
	
	
	
	
	public static void countNodeAndEdges(SerializableGraph sg){
		Collection<Vertex> vs = sg.getAllVertexs();
		int nodeCounter = vs.size();
		int edgeCounter = 0;
		for(Vertex v : vs){
			if(sg.getNeighborsByVertex(v)!=null){
				edgeCounter+=sg.getNeighborsByVertex(v).size();
			}
		}
		System.out.println(nodeCounter + ", " + edgeCounter);
	}
	
	public static Integer[] getDegreeDis(SerializableGraph sg, int maxD){
		Collection<Vertex> vs = sg.getAllVertexs();
		Integer[] degrees = new Integer[maxD];
		for(int i=0; i<maxD; i++){
			degrees[i] = 0;
		}
		for(Vertex v : vs){
			if(sg.getNeighborsByVertex(v) != null){
				degrees[sg.getNeighborsByVertex(v).size()-1] += 1;
			}
		}
		return degrees;
	}
	
	public static  void writeToFile(int[] arr, String fileName){
		ArrayList<Integer> array = new ArrayList<>();
		for(int i=0; i<arr.length; i++){
			array.add(i);
		}
		FileHelper.writeFile(fileName, array);
	}
	//获取父亲节点
	public static HashMap<Vertex, HashSet<Vertex>> getOntologyChildFathers(SerializableGraph sg, Entity.TYPE ontologyType, UniformRelation.TYPE type){
		//is_a, part_of
		log.info("get ontology " + ontologyType + " child and father map...");
		Collection<Vertex> vs = sg.getAllVertexs();
		HashMap<Vertex, HashSet<Vertex>> cf = new HashMap<>();
		for(Vertex v : vs){
			if(v.isInNodeTypes(ontologyType.getTag())){
				LinkedList<EdgeInfo> edges = sg.getNeighborsByVertex(v);
				for(EdgeInfo eg : edges){
					if(eg.isInRelType(type.getTag())){
						if(cf.containsKey(v)){
							cf.get(v).add(eg.getVertex());
						}else{
							HashSet<Vertex> set = new HashSet<>();
							set.add(eg.getVertex());
							cf.put(v, set);
						}
					}
				}
			}
		}
		return cf;
	}
	
	
	public static HashMap<Vertex, HashSet<EdgeInfo>> getOntologyVertexGeneInfo(SerializableGraph sg, Entity.TYPE ontologyType){
		//is_a, part_of
		log.info("get ontology " + ontologyType + " node and gene map...");
		Collection<Vertex> vs = sg.getAllVertexs();
		HashMap<Vertex, HashSet<EdgeInfo>> cf = new HashMap<>();
		for(Vertex v : vs){
			if(v.isInNodeTypes(ontologyType.getTag())){
				LinkedList<EdgeInfo> edges = sg.getNeighborsByVertex(v);
				for(EdgeInfo eg : edges){
					if(eg.isInRelType(ValidData.getValidFilter()) && eg.getVertex().isInNodeTypes(Entity.TYPE.GENE_ID.getTag())){
						if(cf.containsKey(v)){
							cf.get(v).add(eg);
						}else{
							HashSet<EdgeInfo> set = new HashSet<>();
							set.add(eg);
							cf.put(v, set);
						}
					}
				}
			}
		}
		return cf;
	}
	
	static class BubbleItem{
		public Vertex v;
		public int depth;
		public Vertex getV() {
			return v;
		}
		public void setV(Vertex v) {
			this.v = v;
		}
		public int getDepth() {
			return depth;
		}
		public void setDepth(int depth) {
			this.depth = depth;
		}
		public BubbleItem(Vertex v, int depth) {
			super();
			this.v = v;
			this.depth = depth;
		}
		
	}
	
	public static SerializableGraph getBubbledGeneGraph(SerializableGraph sg, Entity.TYPE ontologyType, UniformRelation.TYPE relType, int maxSteps, double minScore){
		HashMap<Vertex, HashSet<Vertex>> cf = getOntologyChildFathers(sg, ontologyType, relType);
		HashMap<Vertex, HashSet<EdgeInfo>> cgene = getOntologyVertexGeneInfo(sg, ontologyType);
		Set<Entry<Vertex, HashSet<EdgeInfo>>> entries = cgene.entrySet();
		log.info("bubble up ...");
		for(Entry<Vertex, HashSet<EdgeInfo>> entry : entries){
			Vertex v = entry.getKey();
			HashSet<EdgeInfo> edges = entry.getValue();
			HashSet<Vertex> fs = cf.get(v);
			log.info("add "+ v + " father gene...");
			if(fs != null){
				Queue<BubbleItem> queue = new LinkedList<BubbleItem>();
				for(Vertex tempV : fs){
					queue.add(new BubbleItem(tempV, 1));
				}
				HashSet<Vertex> maker = new HashSet<>();
				maker.addAll(fs);
				while (queue.size() != 0) {
					// 做缓存，增加遍历速度
					BubbleItem curF = queue.poll();
					//add gene
					for(EdgeInfo e : edges){
						EdgeInfo tempE = new EdgeInfo(e.getVertex(), 
								e.getScore()*UniformRelation.getScoreByType(relType)*UniformRelation.getGlobalWeight(relType));
						//权值太小也不行
						if(tempE.getScore() < minScore){
							continue;
						}
						addEdgeToNetWork(sg, curF.v.getUmlsId(), tempE, true);
					}
					//add to father's father
					HashSet<Vertex> ff = cf.get(curF.v);
					if(ff != null){
						for(Vertex future:ff){
							if(!maker.contains(future)){
								if(curF.depth < maxSteps){
									//向上冒泡，直到最大深度
									queue.add(new BubbleItem(future, curF.depth+1));
									maker.add(future);
								}
							}
						}
					}
				}
			}
		}
		return sg;
	}
	
	public static void removeEdgeFromNetWork(SerializableGraph sg,
			String diseaseUmls, EdgeInfo edgeinfo) {
		Vertex v = sg.getVertexByUmls(diseaseUmls);
		LinkedList<EdgeInfo> edges = sg.getNeighborsByVertex(v);
		for(EdgeInfo e : edges){
			if (e.getVertex().getUid() == edgeinfo.getVertex().getUid()) {
				edgeinfo = e;
				break;
			}
		}
		edges.remove(edgeinfo);
		Vertex rev = edgeinfo.getVertex();
		LinkedList<EdgeInfo> revEdges = sg.getNeighborsByVertex(sg.getVertexByUmls(rev.getUmlsId()));
		EdgeInfo tempInfo = null;
		for (EdgeInfo e : revEdges) {
			if (e.getVertex().getUid() == v.getUid()) {
				tempInfo = e;
				break;
			}
		}
		revEdges.remove(tempInfo);
	}

	
	public static void removeEdgeFromNetWork(SerializableGraph sg,
			String diseaseUmls, Vertex f) {
		EdgeInfo edgeinfo = null;
		Vertex v = sg.getVertexByUmls(diseaseUmls);
		LinkedList<EdgeInfo> edges = sg.getNeighborsByVertex(v);
		for(EdgeInfo e : edges){
			if (e.getVertex().getUid() == f.getUid()) {
				edgeinfo = e;
				break;
			}
		}
		edges.remove(edgeinfo);
		Vertex rev = edgeinfo.getVertex();
		LinkedList<EdgeInfo> revEdges = sg.getNeighborsByVertex(sg.getVertexByUmls(rev.getUmlsId()));
		EdgeInfo tempInfo = null;
		for (EdgeInfo e : revEdges) {
			if (e.getVertex().getUid() == v.getUid()) {
				tempInfo = e;
				break;
			}
		}
		revEdges.remove(tempInfo);
	}
	
	public static void addEdgeToNetWork(SerializableGraph sg,
		String diseaseUmls, EdgeInfo edgeinfo, boolean reverse) {
		Vertex v = sg.getVertexByUmls(diseaseUmls);
		sg.addEdge(v, edgeinfo);
		if(reverse){
			Vertex rev = edgeinfo.getVertex();
			EdgeInfo e = new EdgeInfo(v, edgeinfo.getScore());
			e.setRelType(edgeinfo.getRelType());
			sg.addEdge(rev, e);
		}
	}
	
}
